const mongo = require("../../lib/mongo");
const USER = require("../object/user");
const validate = require('koa2-validation');
const Joi = require('joi');
const mid = require("../middleware");
const ctrl = {};
/**
 * 用户登录接口
 * 主要逻辑，返回二维码，用户扫描后返回用户信息和eventKey，用户通过eventKey访问Session接口设置Session从而完成整个登录逻辑
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.login = async (ctx) => {
    try{
        let eventKey = `login-${fun.randomString()}-${new Date().getTime()}`;
        ctx.set('Connection','keep-alive');
        ctx.set('Cache-Control','no-cache');
        ctx.set('Content-Type', 'text/event-stream');
        ctx.status = 200;
        let stamp = 0;
        await new Promise(function (resolve, reject) {
            wxApi.createTmpQRCode(eventKey, 60 * 5 + 10, async (err, result)=>{
                if(err)reject(err);
                stamp = new Date().getTime();
                ctx.res.write("event: qrCode\n" + "id: " + eventKey + "\n" + "data: " + JSON.stringify({
                    url:wxApi.showQRCodeURL(result.ticket)
                }) + "\n\n");
                resolve(null);
            });
        });
        let counter = setInterval(function(){
            let less = stamp + 60 * 5 * 1000 - new Date().getTime();
            let time = new Date(less), min = time.getMinutes() + "", sec = time.getSeconds() + "";

            ctx.res.write("event: count\n" + "id: " + eventKey + "\n" + "data: " + min.padStart(2, '0') + ":" + sec.padStart(2, '0') + "\n\n");
        }, 1000), countTimeout = null;
        let openId = await Promise.race([
            new Promise(function (resolve, reject) {
                countTimeout = setTimeout(() => {
                    fun.event.removeListener(eventKey, (data) => {
                        clearInterval(counter);
                        resolve(data.openId)
                    });
                    clearInterval(counter);
                    resolve(null)
                }, stamp + 60 * 5 * 1000 - new Date().getTime());
            }), new Promise(function (resolve, reject) {
                fun.event.once(eventKey, (data) => {
                    clearInterval(counter);
                    countTimeout && clearTimeout(countTimeout);
                    resolve(data.openId)
                });
            }), new Promise(function (resolve, reject) {
                ctx.res.on("close", () => {
                    console.log("connection closed by client");
                    fun.event.removeListener(eventKey, (data) => {
                        clearInterval(counter);
                        resolve(data.openId)
                    });
                    clearInterval(counter);
                    countTimeout && clearTimeout(countTimeout);
                    resolve(null)
                });
            })
        ]);
        if(openId){
            let wechatUser = new USER(openId);
            let wxUserInfo = await  new Promise(function (resolve, reject) {
                wxApi.getUser(openId, (err, data)=>{
                    if(err) reject(err);
                    resolve(data);
                });
            });
            ctx.res.write("event: info\n" + "data: 扫描二维码成功，正在登陆中...\n\n");
            await fun.sleep(1);
            let userInfo = await wechatUser.login(wxUserInfo);
            let newEventKey = `login${fun.randomString()}${new Date().getTime()}`;
            console.log(userInfo, !userInfo.value);
            if(!userInfo.value){
                const appKey = await wechatUser.generateAppKey();
                ctx.res.write(`event: info\ndata: ${wxUserInfo.nickname}，很荣幸您能关注我们的产品\n\n`);
                let newUser = await wechatUser.updateUserInfo({
                    nickname:wxUserInfo.nickname
                    , ico:wxUserInfo.headimgurl
                    , sex:wxUserInfo.sex
                    , city:wxUserInfo.city
                    , province:wxUserInfo.province
                    , country:wxUserInfo.country
                    , created_at:new Date()
                    , first:true
                    , loginTime:0
                    , appKey
                });
                userInfo = Object.assign({}, newUser);
                await fun.sleep(1);
                newUser.newEventKey = newEventKey;
                ctx.res.write(`event: user\ndata: ${JSON.stringify(newUser)}\n\n`);
                ctx.res.write(`event: pack\ndata: []\n\n`);
                ctx.res.write(`event: end\ndata: end\n\n`);
            } else {
                userInfo = userInfo.value;
                ctx.res.write(`event: info\ndata: ${wxUserInfo.nickname}，欢迎回来\n\n`);
                await wechatUser.newLoginLog();
                userInfo.newEventKey = newEventKey;
                if(userInfo.tel) userInfo.tel = userInfo.tel.substr(0,3) + "****" + userInfo.tel.substr(7,4);
                await fun.sleep(1);
                ctx.res.write(`event: user\ndata: ${JSON.stringify(userInfo)}\n\n`);
                let userPackList = await wechatUser.getPackList();
                ctx.res.write(`event: pack\ndata: ${JSON.stringify(userPackList)}\n\n`);
                ctx.res.write(`event: end\ndata: end\n\n`);
            }
            countTimeout = null;
            //等待用户通过session接口获取用户信息
            await Promise.race([
                new Promise(function (resolve, reject) {
                    countTimeout = setTimeout(() => {
                        fun.event.removeListener(newEventKey, (cb) => {
                            cb(userInfo);
                            resolve(null)
                        });
                    }, 60 * 1000);
                }), new Promise(function (resolve, reject) {
                    fun.event.once(newEventKey, (cb) => {
                        countTimeout && clearTimeout(countTimeout);
                        cb(userInfo);
                        resolve(null)
                    });
                })
            ]);
        } else {
            ctx.res.end();
        }
    }catch(e){
        throw e;
    }
};
/**
 * 用户session接口
 * @param ctx
 * @returns {Promise<undefined>}
 */
ctrl.session = async (ctx) => {
    try{
        await new Promise(function (resolve, reject) {
            try{
                fun.event.emit(ctx.params.EventKey, (data) => {
                    ctx.session.userInfo = data;
                    resolve(null)
                });
            }catch(e){
                reject(e)
            }
        });
        return await fun.res(ctx, '00000');
    }catch(e){
        throw e;
    }
};
/**
 * 用户退出
 * @param ctx
 * @returns {Promise<undefined>}
 */
ctrl.logout = async (ctx) => {
    try{
        delete ctx.session.userInfo;
        return await fun.res(ctx, '00000');
    }catch(e){
        throw e;
    }
};
/**
 * 获取验证码
 * @param ctx
 * @returns {Promise<undefined>}
 */
ctrl.phoneCode = async (ctx)=>{
    try{
        const codeDB = new mongo(mydb, 'app.user.phone.code');
        let phoneReg=/^[1][3,4,5,7,8][0-9]{9}$/;
        if (!phoneReg.test(ctx.params.tel)) return await fun.res(ctx, '10005');
        const user = new mongo(mydb, 'app.user');
        let hasUser = await user.findOne({tel:ctx.params.tel});
        if(hasUser) return await fun.res(ctx, '10008');
        if(!ctx.session.userInfo) return await fun.res(ctx, '10003');
        let lastCode = await codeDB.findOne({$or:[{tel:ctx.params.tel}, {'user.openId':ctx.session.userInfo.openId}]}, {sort:{created_at:-1}});
        if(lastCode  && ((new Date().getTime() - lastCode.created_at.getTime()) < 5 * 60 * 1000)) return await fun.res(ctx, '10002');
        let code = fun.randomString(4);
        let newCode = await codeDB.insert({
            tel:ctx.params.tel
            , code
            , user:{
                nickname:ctx.session.userInfo.nickname
                , openId:ctx.session.userInfo.openId
                , _id:fun.ObjectId(ctx.session.userInfo._id)
            }
            , created_at: new Date()
        });
        let res = await fun.smsClient.sendSMS({
            PhoneNumbers: ctx.params.tel,
            SignName: 'prxox',
            TemplateCode: 'SMS_133855077',
            TemplateParam: `{"code":"${code}"}`
        });
        await codeDB.update({_id:newCode.ops[0]._id}, {$set:{res}});
        if(res.Code !== "OK") return await fun.res(ctx, '10004', res);
        return await fun.res(ctx, '00000');
    }catch(e){
        throw e;
    }
};
/**
 * 更新用户的手机号码
 * @param ctx
 * @returns {Promise<undefined>}
 */
ctrl.updateUserInfo = async (ctx)=>{
    try{
        const codeDB = new mongo(mydb, 'app.user.phone.code');
        let lastCode = await codeDB.findOne({tel:ctx.request.body.tel}, {sort:{created_at:-1}});
        if(!lastCode) return await fun.res(ctx, '10006');
        if((new Date().getTime() - lastCode.created_at.getTime()) > 5 * 60 * 1000) return await fun.res(ctx, '10006');
        if(lastCode.code !== ctx.request.body.code) return await fun.res(ctx, '10007');
        const user = new mongo(mydb, 'app.user');
        ctx.session.userInfo.first = false;
        ctx.session.userInfo.tel = ctx.request.body.tel.substr(0,3) + "****" + ctx.request.body.tel.substr(7,4);
        await user.update({_id:fun.ObjectId(ctx.session.userInfo._id)}, {$set:{first:false, tel:ctx.request.body.tel}});
        return await fun.res(ctx, '00000');
    }catch (e) {
        throw e;
    }
};
/**
 * 更新AppKey
 * @returns {Promise<void>}
 */
ctrl.resetAppKey = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        const appKey = await wechatUser.generateAppKey();
        let userInfo = await wechatUser.updateUserInfo({appKey});
        ctx.session.userInfo = userInfo;
        return await fun.res(ctx, '00000', userInfo);
    }catch(e){
        throw e;
    }
};
/**
 * 生成订单
 * @param ctx
 * amount       金额
 * couponCode   优惠码
 * type         支付方式
 * @returns {Promise<undefined>}
 */
ctrl.createdPayment = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let body = ctx.request.body;
        body.uid = fun.ObjectId(ctx.session.userInfo._id);
        let payment = await wechatUser.createPayment(body);
        return await fun.res(ctx, '00000', payment);
    }catch(e){
        throw e;
    }
};
/**
 * 监听订单完成事件。当第三方支付回调完成后会触发事件，当此接口接收到触发事件时则完成监听
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.listenPayment = async (ctx)=>{
    try{
        ctx.set('Connection','keep-alive');
        ctx.set('Cache-Control','no-cache');
        ctx.set('Content-Type', 'text/event-stream');
        ctx.status = 200;
        let eventName = ctx.params.event, time = 1;
        let counter = setInterval(function(){
            ctx.res.write("event: wait\n" + "data: " + time + "\n\n");
            time++;
        }, 1000);
        let countTimeout;
        await Promise.race([
            new Promise(function (resolve, reject) {
                countTimeout = setTimeout(async () => {
                    fun.event.removeListener(eventName, (data) => {});
                    clearInterval(counter);
                    await fun.sleep(1);
                    ctx.res.write("event: end\n" + "data: " + JSON.stringify({res:"timeout"}) + "\n\n");
                    resolve(null)
                }, 60 * 2 * 1000);
            }), new Promise(function (resolve, reject) {
                fun.event.once(eventName, async (data) => {
                    clearInterval(counter);
                    clearTimeout(countTimeout);
                    await fun.sleep(1);
                    //事件返回
                    switch(data.type){
                        case 1:
                            ctx.res.write("event: end\n" + "data: " + JSON.stringify(data) + "\n\n");
                            break;
                        default:
                    }
                    resolve(null)
                });
            }), new Promise(function (resolve, reject) {
                ctx.res.on("close", () => {
                    console.log("connection closed by client");
                    fun.event.removeListener(eventName, (data) => {});
                    clearInterval(counter);
                    clearTimeout(countTimeout);
                    resolve(null);
                });
            })
        ]);

        ctx.res.end();
    }catch (e) {
        console.error(e);
        throw e;
    }
};
/**
 * 第三方支付触发回调接口
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.paymentCallback = async (ctx)=>{};
/**
 * 直冲支付触发完成事件接口
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.paymentFinish = async (ctx)=>{
    await fun.sleep(3);
    let money = await USER.finishPayment(ctx.params.paymentId, {type:1});
    fun.event.emit(`payment-${ctx.params.paymentId}`, {paymentId:ctx.params.paymentId, res:'success', type:1, money});
    return await fun.res(ctx, '00000');
};
/**
 * 更新session中的金额信息
 * @param ctx
 * @returns {Promise<undefined>}
 */
ctrl.updateUserBalance = async (ctx)=>{
    ctx.session.userInfo.money = ctx.request.body;
    return await fun.res(ctx, '00000');
};
/**
 * 用于前端更新套餐使用情况
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.getUserPack = async (ctx) => {
    try{
        ctx.set('Connection','keep-alive');
        ctx.set('Cache-Control','no-cache');
        ctx.set('Content-Type', 'text/event-stream');
        ctx.status = 200;
        let skip = parseInt(ctx.params.index);
        let wechatUser = new USER(ctx.session.userInfo.openId);
        const packInfo = async ()=>{
            let userPackList = await wechatUser.getSinglePack(skip);
            ctx.res.write(`event: pack\ndata: ${JSON.stringify(userPackList)}\n\n`);
        };
        const useInfo = async ()=>{
            let useLog = await wechatUser.useLog();
            ctx.res.write(`event: useLog\ndata: ${JSON.stringify(useLog)}\n\n`);
        };
        let counter1 = setInterval(packInfo, 30000);
        let counter2 = setInterval(useInfo, 30000);
        await packInfo();
        await useInfo();
        await new Promise(function (resolve, reject) {
            ctx.res.on("close", () => {
                console.log("connection closed by client");
                clearInterval(counter1);
                clearInterval(counter2);
                resolve(null)
            });
        });
    }catch(e){
        throw e;
    }
};
/**
 * 用户使用记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.useLog = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.getUseProxyLog(ctx.params.page);
        return await fun.res(ctx, '00000', object);
    }catch (e) {
        throw e;
    }
};
/**
 * 用户充值记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.paymentList = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.getPaymentList(ctx.params.page);
        return await fun.res(ctx, '00000', object);
    }catch (e) {
        throw e;
    }
};
/**
 * 用户购买记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.buyLog = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.getBuyList(ctx.params.page);
        return await fun.res(ctx, '00000', object);
    }catch (e) {
        throw e;
    }
};
/**
 * 用户添加缓存记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.cacheAdd = async (ctx) => {
    try {
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.addCache(ctx.params.id);
        if (object && object.error) {
            switch (object.error) {
                case "the cache was already exist":
                    return await fun.res(ctx, '10012');
                    break;
                case "the request method is not surpport":
                    return await fun.res(ctx, '10013');
                    break;
            }
        } else {
            return await fun.res(ctx, '00000', object);
        }
    } catch (e) {
        throw e;
    }
};
/**
 * 用户删除缓存记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.cacheRemove = async (ctx) => {
    try {
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.removeCache(ctx.params.id);
        return await fun.res(ctx, '00000', object);
    } catch (e) {
        throw e;
    }
};
/**
 * 用户缓存记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.cacheList = async (ctx) => {
    try {
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.getCacheList(ctx.params.page);
        return await fun.res(ctx, '00000', object);
    } catch (e) {
        throw e;
    }
};
/**
 * 用户搜索缓存记录
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.cacheSearch = async (ctx) => {
    try {
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.getCacheList(ctx.params.page, null, ctx.request.body.params);
        return await fun.res(ctx, '00000', object);
    } catch (e) {
        throw e;
    }
};
/**
 * 正则过滤列表
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.regularList = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let object = await wechatUser.regularList(ctx.params.page);
        return await fun.res(ctx, '00000', object);
    }catch (e) {
        throw e;
    }
};
/**
 * 创建新的正则过滤
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.newRegular = async (ctx)=>{
    try{
        let body = ctx.request.body;
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let resBody = await wechatUser.updateRegular(body.rule);
        return await fun.res(ctx, '00000', resBody);
    }catch (e) {
        throw e;
    }
};
/**
 * 修改正则过滤
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.updateRegular = async (ctx)=>{
    try{
        let body = ctx.request.body;
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let resBody = await wechatUser.updateRegular(body.rule, ctx.params.id);
        return await fun.res(ctx, '00000', resBody);
    }catch (e) {
        throw e;
    }
};
/**
 * 删除正则过滤
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.removeRegular = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let resBody = await wechatUser.removeRegular(ctx.params.id);
        return await fun.res(ctx, '00000', resBody);
    }catch (e) {
        throw e;
    }
};

/**
 * 删除正则过滤
 * @param ctx
 * @returns {Promise<void>}
 */
ctrl.dayUseList = async (ctx)=>{
    try{
        let wechatUser = new USER(ctx.session.userInfo.openId);
        let resBody = await wechatUser.useLog();
        return await fun.res(ctx, '00000', resBody);
    }catch (e) {
        throw e;
    }
};

module.exports = (_, pre = '/app/api/') =>{
    _.get(pre + 'login', ctrl.login);
    _.get(pre + 'getSession/:EventKey', ctrl.session);
    _.get(pre + 'logout', ctrl.logout);
    _.get(pre + 'phone/code/:tel', mid.checkLogin,  ctrl.phoneCode);
    _.post(pre + 'user/info', mid.checkLogin, ctrl.updateUserInfo);
    _.get(pre + 'user/resetAppKey', mid.checkLogin, ctrl.resetAppKey);
    _.get(pre + 'user/package/:index', mid.checkLogin, ctrl.getUserPack);
    //整个订单的逻辑 创建 =》 监听订单状态 =》 支付 =》 完成
    _.post(pre + 'user/payment/create', validate({
        body: {
            amount: Joi.number().required(),
            type: Joi.number().required()
        }
    }), mid.checkLogin, ctrl.createdPayment);
    _.get(pre + 'user/payment/listen/:event', mid.checkLogin, ctrl.listenPayment);
    _.post(pre + 'user/payment/callback', mid.checkLogin, ctrl.paymentCallback);
    _.get(pre + 'user/payment/finish/:paymentId', mid.checkLogin, ctrl.paymentFinish);
    //订单列表
    _.get(pre + 'user/payment/list/:page', mid.checkLogin, ctrl.paymentList);
    //更新Session中的值
    _.post(pre + 'user/updateUserBalance', mid.checkLogin, ctrl.updateUserBalance);
    //使用记录
    _.get(pre + 'user/use/log/:page', mid.checkLogin, ctrl.useLog);
    // 购买记录
    _.get(pre + 'user/buy/log/:page', mid.checkLogin, ctrl.buyLog);
    //缓存设置
    _.get(pre + 'user/cache/add/:id', mid.checkLogin, ctrl.cacheAdd);
    _.get(pre + 'user/cache/list/:page', mid.checkLogin, ctrl.cacheList);
    _.post(pre + 'user/cache/list/:page', mid.checkLogin, ctrl.cacheSearch);
    _.get(pre + 'user/cache/remove/:id', mid.checkLogin, ctrl.cacheRemove);
    //正则过滤
    _.get(pre + 'user/regular/list/:page', mid.checkLogin, ctrl.regularList);
    _.post(pre + 'user/regular/new', mid.checkLogin, ctrl.newRegular);
    _.post(pre + 'user/regular/modify/:id', mid.checkLogin, ctrl.updateRegular);
    _.get(pre + 'user/regular/del/:id', mid.checkLogin, ctrl.removeRegular);
    _.get(pre + 'user/use/log', mid.checkLogin, ctrl.dayUseList);
};